package com.syh.jn.scm.infrastructure.repository.impl.supplier.stock;

import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import cn.redscarf.commons.wrapper.expression.orderExpression.Order;
import com.syh.core.ddd.infrastructure.repository.AbstractAggregateRepository;
import com.syh.jn.scm.domain.supplier.entity.stock.StockGoods;
import com.syh.jn.scm.domain.supplier.entity.stock.StockRecord;
import com.syh.jn.scm.domain.supplier.entity.stock.StockRecordItem;
import com.syh.jn.scm.domain.supplier.repository.stock.StockGoodsRepository;
import com.syh.jn.scm.dto.supplier.stock.StockGoodsVo;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.infrastructure.repository.model.logistics.LogisticsBillModel;
import com.syh.jn.scm.infrastructure.repository.model.stock.StockGoodsModel;
import com.syh.jn.scm.infrastructure.repository.model.stock.StockRecordItemModel;
import com.syh.jn.scm.infrastructure.repository.utils.MybatisTemplate;
import com.syh.jn.scm.infrastructure.repository.utils.SQLAliasGenerateUtil;
import com.syh.jn.scm.infrastructure.wrapper.BaseResolve;
import com.syh.jn.scm.infrastructure.wrapper.ResolveToSQL;
import com.syh.jn.scm.service.api.supplier.stock.enums.StockGoodsRecordEnums;
import org.apache.ibatis.jdbc.SQL;
import org.hibernate.Query;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 库存管理
 */
@Repository
public class StockGoodsRepositoryImpl extends AbstractAggregateRepository<StockGoods, StockGoodsModel, Long> implements StockGoodsRepository {
  @Override
  protected SQL getViewSql() {
    return new SQL() {{
      SELECT(
              SQLAliasGenerateUtil.aliasGenerate(StockGoodsModel.class, "goods", null),
              "storeHouse.NAME AS storehouseName",
              "cate.NAME AS categoryName",
              "brand.NAME AS goodsBrandName",
              "manufacturer.NAME AS manufacturerName",
              "origin.NAME AS originName",
              "storeHouse.IS_PERCHASE_TO_SALE AS isPerchaseToSale"
      );
      FROM("T_STOCK_STOCK_GOODS goods");
      LEFT_OUTER_JOIN(
              "T_BASE_STOREHOUSE storeHouse on storeHouse.ID = goods.STOREHOUSE_ID",
              "T_BASE_GOODS_BRAND brand on brand.ID = goods.GOODS_BRAND_ID",
              "T_BASE_MANUFACTURER manufacturer on manufacturer.ID = goods.MANUFACTURER_ID",
              "T_BASE_ORIGIN origin on origin.ID = goods.ORIGIN_ID",
              "T_BASE_GOODS_CATEGORY cate on goods.CATEGORY_ID = cate.ID"
      );
    }};
  }

  @Override
  protected Class getViewObject() {
    return StockGoodsVo.class;
  }

  @Override
  protected BaseResolve getViewWrapperResolve(Wrapper wrapper) {
    HashMap<String, String> map = new HashMap();
    map.put("id", "goods.ID");
    map.put("storehouseId", "goods.STOREHOUSE_ID");
    map.put("categoryId", "goods.CATEGORY_ID");
    map.put("name", "goods.NAME");
    map.put("goodsId", "goods.GOODS_ID");
    map.put("status", "goods.STATUS");
    map.put("originId", "goods.ORIGIN_ID");
    map.put("manufacturerId", "goods.MANUFACTURER_ID");
    map.put("goodsBrandId", "goods.GOODS_BRAND_ID");
    map.put("businessEntityId", "goods.BUSINESS_ENTITY_ID");
    ResolveToSQL parse = new ResolveToSQL(wrapper, (propertyName) -> {
      return map.get(propertyName);
    });
    return parse;
  }

  @Override
  public StockGoods getByStorehouseIdAndGoodsId(Long storehouseId, Long goodsId) {
    Wrapper wrapper = Wrapper.build().append(Restrictions.eq("storehouseId", storehouseId)).append(Restrictions.eq("goodsId", goodsId));
    StockGoods stockGoods = this.getList(wrapper).get(0);
    return stockGoods;
  }

  @Override
  public Map<String, Object> statisticalAmount(Wrapper we) {

    SQL sql = new SQL() {{
      SELECT(
              " SUM(BALANCE) AS statisticalTotalAmount"
      );
      FROM("T_STOCK_STOCK_GOODS goods");
    }};
    StringBuffer sqlString = new StringBuffer(sql.toString());
    BaseResolve resolveToSQL = this.getViewWrapperResolve(we);
    sqlString.append(" WHERE 1 = 1").append(resolveToSQL.resolve());
    Map<String, Object> map = MybatisTemplate.selectOne(sqlString.toString());
    return map;
  }

  @Override
  public Map<String, Object> statisticalGoodsType(Wrapper we) {
    SQL sql = new SQL() {{
      SELECT(
              " COUNT(*) AS count"
      );
      FROM("T_BASE_GOODS goods");
    }};
    StringBuffer sqlString = new StringBuffer(sql.toString());
    BaseResolve resolveToSQL = this.getViewWrapperResolve(we);
    sqlString.append(" WHERE 1 = 1").append(resolveToSQL.resolve());
    Map<String, Object> map = MybatisTemplate.selectOne(sqlString.toString());
    return map;
  }

  @Override
  public void recalculationStockGoodsAverageCost() {

    SQL sql = new SQL() {{
      SELECT("record.BUSINESS_ENTITY_ID AS businessEntityId,record.STOREHOUSE_ID AS storehouseId",
              "record.TYPE as orderType,record.ORIGINAL_ORDER_ID as orderId,record.ORIGINAL_LOGISTICS_ID AS logisId,record.DATA_TYPE AS dataType",
              "recordItem.GOODS_ID AS goodsId,recordItem.GOODS_NAME AS goodsName,recordItem.QUANTITY AS quantity,recordItem.ID AS itemId",
              "CASE record.TYPE WHEN 1 THEN saleItem.PRICE " +
                      "WHEN 2 THEN saleRetItem.PRICE " +
                      "WHEN 3 THEN purItem.PRICE " +
                      "WHEN 4 THEN purRetItem.PRICE " +
                      "WHEN 7 THEN otherInItem.PRICE END AS price");
      FROM("T_STOCK_STOCK_RECORD record ");
      INNER_JOIN("T_STOCK_STOCK_RECORD_ITEM recordItem on record.ID = recordItem.RECORD_ORDER_ID");
      LEFT_OUTER_JOIN("T_SALE_ORDER_ITEM saleItem on recordItem.ORDER_ITEM_ID = saleItem.ID");
      LEFT_OUTER_JOIN("T_SALE_RETURN_ORDER_ITEM saleRetItem on recordItem.ORDER_ITEM_ID = saleRetItem.ID");
      LEFT_OUTER_JOIN("T_PUR_PURCHASE_ORDER_ITEM purItem on recordItem.ORDER_ITEM_ID = purItem.ID");
      LEFT_OUTER_JOIN("T_PUR_PURCHASE_RETURN_ORDER_ITEM purRetItem on recordItem.ORDER_ITEM_ID = purRetItem.ID");
      LEFT_OUTER_JOIN("T_STOCK_OTHER_IN_BILL_ITEM otherInItem on recordItem.ORDER_ITEM_ID = otherInItem.ID");
      ORDER_BY("record.CREATE_TIME_STAMP ASC");
    }};
    List<Map<String, Object>> list = MybatisTemplate.selectList(sql.toString());

    //初始化所有库存的数量，库存余额，平均成本
    Query query = buildQuery("update StockGoodsModel model set model.averageCost =:averageCost ,model.balance =:balance,model.quantity =:quantity");
    query.setInteger("averageCost", 0);
    query.setInteger("balance", 0);
    query.setInteger("quantity", 0);
    query.executeUpdate();

    //根据库存记录计算库存的数量，库存余额，平均成本
    list.forEach(map -> {
      Long businessEntityId = (Long) map.get("businessEntityId");
      Long storehouseId = (Long) map.get("storehouseId");
      Long goodsId = (Long) map.get("goodsId");
      Short orderType = new Short(map.get("orderType").toString());
      String hql = "from StockGoodsModel where businessEntityId=? and storehouseId=? and goodsId=?";
      List<StockGoodsModel> stockGoodsModels = (List<StockGoodsModel>) this.hibernateTemplate.find(hql, new Object[]{businessEntityId, storehouseId, goodsId});

      if (stockGoodsModels != null && stockGoodsModels.size() > 0) {
        StockGoodsModel model = stockGoodsModels.get(0);
        StockRecordItemModel recordItemModel = this.hibernateTemplate.get(StockRecordItemModel.class, (Long) map.get("itemId"));
        if ((orderType.equals(StockGoodsRecordEnums.Type.PURCHASE_ORDER.getStatus())
                || orderType.equals(StockGoodsRecordEnums.Type.PURCHASE_RETURN_ORDER.getStatus())
                || orderType.equals(StockGoodsRecordEnums.Type.STOCK_OTHER_IN_BILL.getStatus()))
                && (BigDecimal) map.get("price") != null) {//找不到单价的商品不计算平均成本

          calculateAverageCost(model, (BigDecimal) map.get("quantity"), (BigDecimal) map.get("price"));
          //出入库记录冗余出入库成本单价
          recordItemModel.setPrice((BigDecimal) map.get("price"));
        } else {

          model.setQuantity(model.getQuantity().add((BigDecimal) map.get("quantity")));
          //如果库存数量不为0 计算库存余额
          if (model.getQuantity().compareTo(BigDecimal.ZERO) != 0) {
            model.setBalance(model.getQuantity().multiply(model.getAverageCost()).setScale(2, BigDecimal.ROUND_HALF_UP));
            //否则库存余额为0
          } else {
            model.setBalance(BigDecimal.ZERO);
          }

          //出入库记录冗余出入库成本单价
          //正常出库直接取平均成本
          if ((map.get("dataType") + "").equals(StockGoodsRecordEnums.DataType.NORMAL.toString())) {
            recordItemModel.setPrice(model.getAverageCost());
          }
          //回滚操作取出库记录的出库成本
          else {
            if (map.get("logisId") != null) {
              LogisticsBillModel logis = this.hibernateTemplate.get(LogisticsBillModel.class, (Long) map.get("logisId"));
              if (logis != null)
                try {
                  recordItemModel.setPrice(getPriceBeforeRollback(logis, orderType, goodsId));
                } catch (Exception e) {
                  recordItemModel.setPrice(model.getAverageCost());
                }
              else
                recordItemModel.setPrice(model.getAverageCost());
            } else {
              recordItemModel.setPrice(model.getAverageCost());
            }
          }
        }
        this.hibernateTemplate.update(model);
        this.hibernateTemplate.update(recordItemModel);
      }
    });

  }

  private void calculateAverageCost(StockGoodsModel model, BigDecimal quantity, BigDecimal price) {
    BigDecimal oldQuantity = model.getQuantity();
    model.setQuantity(model.getQuantity().add(quantity));
    if (model.getQuantity().compareTo(BigDecimal.ZERO) != 0) {
      //负库存&零库存出入库以新纪录的成本为准
      if (oldQuantity.compareTo(BigDecimal.ZERO) < 1) {
        model.setAverageCost(price);
        model.setBalance(model.getQuantity().multiply(model.getAverageCost()).setScale(2, BigDecimal.ROUND_HALF_UP));
      } else {
        model.setBalance(model.getBalance().add(quantity.multiply(price).setScale(2, BigDecimal.ROUND_HALF_UP)));
        model.setAverageCost(model.getBalance().divide(model.getQuantity(), 6, BigDecimal.ROUND_HALF_UP));
      }
    }
    //库存为零，成本和库存余额皆为零
    else {
      model.setAverageCost(BigDecimal.ZERO);
      model.setBalance(BigDecimal.ZERO);
    }
  }

  private BigDecimal getPriceBeforeRollback(LogisticsBillModel bill, Short orderType, Long goodsId) {
    StockRecordRepositoryImpl recordRead = (StockRecordRepositoryImpl) SpringContextUtils.getBean(StockRecordRepositoryImpl.class);
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("originalLogisticsId", bill.getId()));
    we.append(Restrictions.eq("storehouseId", bill.getDepotId()));
    we.append(Restrictions.eq("type", orderType));
    we.order(Order.desc("createTimeStamp"));
    StockRecord record = recordRead.getOne(we);
    if (!record.getDataType().equals(StockGoodsRecordEnums.DataType.NORMAL.getStatus()))
      throw new BusinessException("回滚失败，出入库记录异常！");

    //多个相同商品取价格平均
    BigDecimal goodsTotalNum = BigDecimal.ZERO;
    BigDecimal goodsTotalAmt = BigDecimal.ZERO;
    for (StockRecordItem item : record.getItems()) {
      if (item.getGoodsId().equals(goodsId)) {
        goodsTotalNum = goodsTotalNum.add(item.getQuantity());
        goodsTotalAmt = goodsTotalAmt.add(item.getPrice().multiply(item.getQuantity()).setScale(2, BigDecimal.ROUND_HALF_UP));
      }
    }
    BigDecimal goodsAvgPrice = BigDecimal.ZERO;
    if (goodsTotalNum.compareTo(BigDecimal.ZERO) != 0) {
      goodsAvgPrice = goodsAvgPrice.add(goodsTotalAmt.divide(goodsTotalNum, 6, BigDecimal.ROUND_HALF_UP));
    }

    return goodsAvgPrice;
  }

}
